﻿// Copyright 2012 Stuart Turner
// Licensed under the Apache License, Version 2.0 [http://www.apache.org/licenses/LICENSE-2.0]

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.IO;

using Complete.Data.Support;

namespace Complete.Data.Linq.Expressions
{
	/// <summary>
	/// Writes out an expression tree (including DbExpression nodes) in a C#-ish syntax
	/// </summary>
	public class CompleteExpressionWriter : ExpressionWriter
	{
		protected CompleteExpressionWriter(TextWriter writer)
			: base(writer)
		{
		}

		public new static void Write(TextWriter writer, Expression expression)
		{
			new CompleteExpressionWriter(writer).Visit(expression);
		}

		public new static string WriteToString(Expression expression)
		{
			StringWriter sw = new StringWriter();
			Write(sw, expression);
			return sw.ToString();
		}

		protected override Expression Visit(Expression exp)
		{
			switch ((CompleteExpressionType)exp.NodeType)
			{
				case CompleteExpressionType.Select:
					return this.VisitSelect((SelectExpression)exp);

				case CompleteExpressionType.Where:
					return this.VisitWhere((WhereExpression)exp);

				case CompleteExpressionType.From:
					return this.VisitFrom((FromExpression)exp);

				case CompleteExpressionType.Column:
					return this.VisitColumn((ColumnExpression)exp);

				case CompleteExpressionType.Table:
					return this.VisitTable((TableExpression)exp);

				case CompleteExpressionType.Join:
					return this.VisitJoin((JoinExpression)exp);

				default:
					return base.Visit(exp);
			}
		}

		protected virtual Expression VisitFrom(FromExpression from)
		{
			this.Write(".From(");
			this.WriteLine(Indentation.Inner);
			this.Visit(from.From);
			this.WriteLine(Indentation.Outer);
			this.Write(")");
			return from;
		}

		protected virtual Expression VisitJoin(JoinExpression join)
		{
			this.Write("." + join.JoinType + "(");
			this.WriteLine(Indentation.Inner);
			this.Visit(join.Left);
			this.Write(",");
			this.WriteLine(Indentation.Same);
			this.Visit(join.Right);

			if (join.Comparison != null)
			{
				this.Write(",");
				this.WriteLine(Indentation.Same);
				this.Visit(join.Comparison);
			}

			this.WriteLine(Indentation.Outer);
			this.Write(")");
			return join;
		}

		protected virtual Expression VisitTable(TableExpression table)
		{
			this.Write(".Table(");
			this.Write(table.Name);
			this.Write(") as ");
			this.Write(table.Alias);
			this.Write(" ");
			return table;
		}

		protected virtual Expression VisitColumn(ColumnExpression exp)
		{
			this.Write(string.Format("[{2}].[{0}({1}) = ", exp.Name, exp.Ordinal, exp.Alias));
			this.Visit(exp.Declaration);
			this.Write("]");
			return exp;
		}

		protected virtual Expression VisitSelect(SelectExpression select)
		{
			this.Write(".Select(");
			this.WriteLine(Indentation.Inner);

			this.Write(".Project(");
			this.WriteLine(Indentation.Inner);
			this.Visit(select.Projection);
			this.WriteLine(Indentation.Outer);
			this.Write("),");
			this.WriteLine(Indentation.Same);

			this.Visit(select.From);

			if (select.Where != null)
			{
				this.Write(",");
				this.WriteLine(Indentation.Same);
				this.Visit(select.Where);
				this.WriteLine(Indentation.Outer);
			}
			this.Write(") as ");
			this.Write(select.Alias);
			return select;
		}

		protected virtual Expression VisitWhere(WhereExpression where)
		{
			this.Write(".Where(");
			this.WriteLine(Indentation.Inner);
			this.Visit(where.Predicate);
			this.WriteLine(Indentation.Outer);
			this.Write(")");
			return where;
		}
	}
}
